#include <iostream>
#include <string>
#include <vector>
#include <WinSock2.h>
#include <Ws2tcpip.h>

#include "CService.h"
#include "protocol.h"
#include "util.h"
#include "boost/foreach.hpp"
#include "boost/algorithm/string/predicate.hpp"

//using namespace std;

static std::vector<SOCKET> vhListenSocket;

bool BindListenPort(const CService &addr)
{
	std::string strError = "";
	struct sockaddr sockaddr;
	int nOne =1;

	int len = sizeof(sockaddr);
	if(!addr.GetSockAddr((struct sockaddr*)&sockaddr, &len))
	{
		std::cout<< "Bind Address family  not supported: " <<std::endl;
		return false;
	}

	SOCKET hListenSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP);
	if(hListenSocket == INVALID_SOCKET)
	{
		std::cout<< "Couldn't open socket for incoming connnections (socket return error "<< WSAGetLastError() << ")" <<std::endl;
		return false;
	}

	setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&nOne, sizeof(int));


	//Set to non-blocking
	if(ioctlsocket(hListenSocket, FIONBIO, (u_long*)&nOne) == SOCKET_ERROR)
	{
		std::cout<< "Couldn't set non-blocking" <<std::endl;
		return false;
	}

	if(::bind(hListenSocket, (struct sockaddr*)&sockaddr, len) ==SOCKET_ERROR)
	{
		std::cout<< "Couldn't bind to port on this computer" <<std::endl;
		return false;
	}

	if(listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR)
	{
		std::cout<< "Listen failed: "<<WSAGetLastError()<<std::endl;
		return false;
	}

	vhListenSocket.push_back(hListenSocket);
	return true;
}

bool GetMyExternalIP()
{
	CService addrConnect;

	addrConnect = CService("61.135.169.105", 80);
	SOCKET hSocketRes = INVALID_SOCKET;
	struct sockaddr sockaddr;
	int len = sizeof(sockaddr);
	if(!addrConnect.GetSockAddr((struct sockaddr*)&sockaddr, &len))
	{
		std::cout<<"Cannot GetSockAddr from class CService"<<std::endl;
		return false;
	}

	SOCKET hSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP);
	if(hSocket == INVALID_SOCKET)
		return false;
	u_long fNonblock =0;
	if(ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR)
	{
		closesocket(hSocket);
		std::cout<< "Couldn't set non-blocking"<<std::endl;
		return false;
	}
	if(connect(hSocket, (struct sockaddr*)&sockaddr, len)==SOCKET_ERROR)
	{
		if(WSAGetLastError() == WSAEWOULDBLOCK)
		{
			//异步SOCKET
			struct timeval timeout;
			timeout.tv_sec = 5;
			timeout.tv_usec = 0;

			fd_set fdset;
			FD_ZERO(&fdset);
			FD_SET(hSocket, &fdset);
			int nRet = select(hSocket+1, NULL, &fdset, NULL, &timeout);
			if(nRet == 0)
			{
				std::cout<< "connect timeout"<<std::endl;
				closesocket(hSocket);
				WSACleanup();
				return false;
			}
			else
			{
				std::cout<<"connected "<<std::endl;
			}
		}
	}

	const char* pszGet="GET / HTTP/1.1\r\n"
		"Host: checkip.dynnds.org\r\n"
		"User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)\r\n"
		"Connection: close\r\n"
		"\r\n";


	if(send(hSocket, pszGet,strlen(pszGet) , 0) == SOCKET_ERROR)
	{
		cout<< "send Error: "<<WSAGetLastError()<<endl;
		return false;
	}

	char c;
	std::string httpres;
	while(true)
	{
		int nBytes = recv(hSocket, &c, 1, 0);
		if(nBytes <= 0)
		{
			break;
		}
		httpres += c;
		if(httpres.size() >= 9000)
			break;
	}

	std::size_t posh, post;
	posh = httpres.find("Current IP Address:");
	if(posh == std::string::npos)
	{
		std::cout<<"Couldn't find IP from http respond"<<std::endl;
	}
	else
	{
		posh += sizeof("Current IP Address:");
		post = httpres.find("</body>");
		httpres = httpres.substr(posh, post-posh);
		std::cout<<"My External IP Addresss is: "<< httpres <<std::endl;
	}
	


	//Turn back to non-blocking
	fNonblock = 0;
	if (ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR)
	{
		closesocket(hSocket);
		return false;
	}
	return true;
}

bool InitializeSocket()
{
	//Initialize Windows Sockets
	WSADATA wsadata;
	int ret = WSAStartup(MAKEWORD(2,2),&wsadata);
	if (ret != NO_ERROR || LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2)
	{
		std::cout<< "WSAStartup error: " << ret <<std::endl;
		return false;
	}
	return true;
}

bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP)
{
	vIP.clear();

	struct addrinfo aiHint;
	memset(&aiHint, 0, sizeof(struct addrinfo));
	aiHint.ai_socktype = SOCK_STREAM;
	aiHint.ai_protocol = IPPROTO_TCP;
	aiHint.ai_family = AF_INET;
	struct addrinfo *aiRes = NULL;
	int nErr = getaddrinfo(pszName, NULL, &aiHint, &aiRes);
	if(nErr)
		return false;
	struct addrinfo *aiTrav = aiRes;
	while(aiTrav != NULL)
	{
		if(aiTrav->ai_family == AF_INET)
		{
			assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in));
			vIP.push_back(CNetAddr(((struct sockaddr_in*)(aiTrav->ai_addr))->sin_addr));
			//for test
			cout<<inet_ntoa(((struct sockaddr_in*)(aiTrav->ai_addr))->sin_addr)<<endl;
		}

		aiTrav = aiTrav->ai_next;
	}

	freeaddrinfo(aiRes);

	return (vIP.size() > 0);
}

bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP)
{
	std::string strHost(pszName);
	if(strHost.empty())
	{
		return false;
	}
	if(boost::algorithm::starts_with(strHost, "[") && boost::algorithm::ends_with(strHost, "]"))
	{
		strHost = strHost.substr(1, strHost.size()-2);
	}

	return LookupIntern(strHost.c_str(), vIP);
}

void DNSAddressSeed()
{
	vector<CDNSSeedData> vSeeds;
	vSeeds.push_back(CDNSSeedData("bitcoin.sipa.be", "seed.bitcoin.sipa.be"));
	vSeeds.push_back(CDNSSeedData("bluematt.me", "dnsseed.bluematt.me"));
	vSeeds.push_back(CDNSSeedData("dashjr.org", "dnsseed.bitcoin.dashjr.org"));
	vSeeds.push_back(CDNSSeedData("bitcoinstats.com", "seed.bitcoinstats.com"));
	vSeeds.push_back(CDNSSeedData("xf2.org", "bitseed.xf2.org"));

	int found = 0;

	BOOST_FOREACH(const CDNSSeedData &seed, vSeeds)
	{
		vector<CNetAddr> vIPs;
		vector<CAddress> vAdd;
		if(LookupHost(seed.host.c_str(), vIPs))
		{
			BOOST_FOREACH(CNetAddr &ip, vIPs)
			{
				int nOneDay = 24*3600;
				CAddress addr = CAddress(CService(ip, 8334));
				//addr.nTime = GetTime() -3*nOneDay - GetRand(4*nOneDay);
				vAdd.push_back(addr);
				found++;
			}
		}

	}
}

int getlocalhost()
{
	struct in_addr inaddr_any;
	inaddr_any.s_addr = INADDR_ANY;
	if( !BindListenPort(CService(inaddr_any, 8334)))
	{
		std::cout<< "Failed to listen on port: " <<std::endl;
		return -1;
	}

	//Get local host IP
	char pszHostName[1000] = "";
	if(gethostname(pszHostName, sizeof(pszHostName)) !=SOCKET_ERROR)
	{
		std::cout <<"Host Name: " <<pszHostName <<std::endl;
	}
	else
	{
		std::cout <<"Get Host Name Error"<<std::endl;
	}

	struct addrinfo aiHint;
	memset(&aiHint, 0, sizeof(struct addrinfo));

	aiHint.ai_socktype = SOCK_STREAM;
	aiHint.ai_protocol = IPPROTO_TCP;
	aiHint.ai_family = AF_INET;
	struct addrinfo *aiRes = NULL;
	int nErr =getaddrinfo(pszHostName, NULL ,&aiHint, &aiRes);
	if(nErr)
	{
		std::cout<<"Failed to getaddrinfo"<<std::endl;
		return false;
	}

	std::cout<<"My Host IP: "<<
		inet_ntoa(((sockaddr_in*)(aiRes->ai_addr))->sin_addr)<<std::endl;
}

int main()
{
	if(!InitializeSocket())
	{
		return -1;
	}

	//getlocalhost();

	
	 
	// GetMyExternalIP();


	 DNSAddressSeed();
	 return 0;
}